package com.gzxx.mongo;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.gzxx.mongo.entity.PageModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @Description MongoDB操作服务接口
 * @Author pengpdx
 * @Date 2020/7/22 9:50
 */
@Component("mongoService")
public class MongoService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    private MongoSqlLogServer mongoSqlLogServer;


    /**
     * 插入或更新
     * 更新是根据ID进行全量更新
     *
     * @param object 对象
     */
    public void insertOrUpdate(Object object) {

        Long time = System.currentTimeMillis();
        String id = (String) ReflectUtil.getFieldValue(object, "id");
        Object objectOrg = StrUtil.isNotEmpty(id) ? mongoTemplate.findById(id, object.getClass()) : null;

        if (objectOrg == null) {
            // 插入
            // 设置插入时间
            if (ReflectUtil.getField(object.getClass(), "createTime") != null) {
                ReflectUtil.setFieldValue(object, "createTime", time);
            }
            if (ReflectUtil.getField(object.getClass(), "updateTime") != null) {
                ReflectUtil.setFieldValue(object, "updateTime", time);
            }

            mongoSqlLogServer.logSave(object);
            mongoTemplate.save(object);
        } else {
            // 更新
            Field[] fields = ReflectUtil.getFields(object.getClass());
            // 拷贝属性
            for (Field field : fields) {
                if (!field.getName().equals("id") && ReflectUtil.getFieldValue(object, field) != null
                        && !field.getName().endsWith("serialVersionUID")) {
                    ReflectUtil.setFieldValue(objectOrg, field, ReflectUtil.getFieldValue(object, field));
                }
            }
            // 设置更新时间
            if (ReflectUtil.getField(objectOrg.getClass(), "updateTime") != null) {
                ReflectUtil.setFieldValue(objectOrg, "updateTime", time);
            }
            mongoSqlLogServer.logSave(objectOrg);
            mongoTemplate.save(objectOrg);
        }

    }



    /**
     * 插入
     *
     * @param object 对象
     */
    public void insert(Object object) {
        String id = (String) ReflectUtil.getFieldValue(object, "id");
        Object objectOrg = StrUtil.isNotEmpty(id) ? mongoTemplate.findById(id, object.getClass()) : null;
        //数据库里已有相同id, 提醒用户插入失败,请检查业务
         Assert.isNull(objectOrg,"The ID data already exists, please check the business!!!");
        insertOrUpdate(object);
    }

    /**
     * 批量插入
     *
     * @param <T>
     */
    public <T> void insertAll(List<T> list) {
        Long time = System.currentTimeMillis();

        Map<String, Object> idMap = new HashMap<String, Object>();
        for (Object object : list) {
            if (ReflectUtil.getFieldValue(object, "id") != null) {
                String id = (String) ReflectUtil.getFieldValue(object, "id");
                Object objectOrg = StrUtil.isNotEmpty(id) ? mongoTemplate.findById(id, object.getClass()) : null;
                idMap.put((String) ReflectUtil.getFieldValue(object, "id"), objectOrg);
            }
        }

        for (Object object : list) {
            // 设置插入时间
            if (ReflectUtil.getField(object.getClass(), "createTime") != null) {
                ReflectUtil.setFieldValue(object, "createTime", time);
            }
            if (ReflectUtil.getField(object.getClass(), "updateTime") != null) {
                ReflectUtil.setFieldValue(object, "updateTime", time);
            }
        }
        mongoSqlLogServer.logSave(list);
        mongoTemplate.insertAll(list);

    }

    /**
     * 根据id更新
     *
     * @param object 对象
     */
    public void updateById(Object object) {
        if (StrUtil.isEmpty((String) ReflectUtil.getFieldValue(object, "id"))) {
            return;
        }
        insertOrUpdate(object);
    }

    /**
     * 根据id更新全部字段
     *
     * @param object 对象
     */
    public void updateAllColumnById(Object object) {

        if (StrUtil.isEmpty((String) ReflectUtil.getFieldValue(object, "id"))) {
            return;
        }

        Long time = System.currentTimeMillis();
        if (ReflectUtil.getField(object.getClass(), "updateTime") != null) {
            ReflectUtil.setFieldValue(object, "updateTime", time);
        }
        mongoSqlLogServer.logSave(object);
        mongoTemplate.save(object);

    }

    /**
     * 根据id删除
     *
     * @param id    对象
     * @param clazz 类
     */
    public void deleteById(String id, Class<?> clazz) {

        if (StrUtil.isEmpty(id)) {
            return;
        }
        deleteByQuery(Criteria.where("id").is(id), clazz);
    }

    /**
     * 根据id删除
     *
     * @param ids    对象
     * @param clazz 类
     */
    public void deleteByIds(List<String> ids, Class<?> clazz) {

        if (ids == null || ids.size() == 0) {
            return;
        }

        deleteByQuery(Criteria.where("id").in(ids), clazz);
    }

    /**
     * 根据条件删除
     *
     * @param query 查询
     * @param clazz 类
     */
    public void deleteByQuery(Query query, Class<?> clazz) {
        mongoSqlLogServer.logDelete(clazz, query);
        mongoTemplate.remove(query, clazz);
    }

    /**
     * 根据条件删除
     *
     * @param criteria 查询
     * @param clazz    类
     */
    public void deleteByQuery(Criteria criteria, Class<?> clazz) {
        deleteByQuery(new Query(criteria), clazz);
    }

    /**
     * 根据条件删除
     *
     * @param criteriaWrapper 查询
     * @param clazz    类
     */
    public void deleteByQuery(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
        deleteByQuery(new Query(criteriaWrapper.build()), clazz);
    }

    /**
     * 清除表数据
     * @param clazz
     */
    public void deleteAll(Class<?> clazz) {
        Query query = new Query();
        mongoTemplate.remove(query, clazz);
    }


    /**
     * 更新查到的第一项
     *
     * @param query  查询
     * @param update 更新
     * @param clazz  类
     */
    public void updateFirst(Query query, Update update, Class<?> clazz) {
        mongoSqlLogServer.logUpdate(clazz, query, update, false);
        mongoTemplate.updateFirst(query, update, clazz);
    }

    /**
     * 更新查到的第一项
     *
     * @param criteria 查询
     * @param update   更新
     * @param clazz    类
     */
    public void updateFirst(Criteria criteria, Update update, Class<?> clazz) {
        updateFirst(new Query(criteria), update, clazz);
    }

    /**
     * 更新查到的第一项
     *
     * @param criteriaWrapper 查询
     * @param update   更新
     * @param clazz    类
     */
    public void updateFirst(CriteriaWrapper criteriaWrapper, Update update, Class<?> clazz) {
        updateFirst(new Query(criteriaWrapper.build()), update, clazz);
    }


    /**
     * 累加某一个字段的数量,原子操作
     * @param id            主键
     * @param property      字段
     * @param count         累加值
     * @param clazz         类
     */
    public void addCountById(String id, String property, Long count, Class<?> clazz) {
        Update update = new Update().inc(property, count);
        updateFirst(new Query(Criteria.where("id").is(id)), update, clazz);
    }

    /**
     * 更新查到的全部项
     *
     * @param query  查询
     * @param update 更新
     * @param clazz  类
     */
    public void updateMulti(Query query, Update update, Class<?> clazz) {
        mongoSqlLogServer.logUpdate(clazz, query, update, true);
        mongoTemplate.updateMulti(query, update, clazz);
    }

    /**
     * 更新查到的全部项
     *
     * @param criteria 查询
     * @param update   更新
     * @param clazz    类
     */
    public void updateMulti(Criteria criteria, Update update, Class<?> clazz) {
        updateMulti(new Query(criteria), update, clazz);
    }

    /**
     * 更新查到的全部项
     *
     * @param criteriaWrapper 查询
     * @param update   更新
     * @param clazz    类
     */
    public void updateMulti(CriteriaWrapper criteriaWrapper, Update update, Class<?> clazz) {
        updateMulti(new Query(criteriaWrapper.build()), update, clazz);
    }


    /**
     * 按查询条件获取Page
     *
     * @param query 查询
     * @param page  分页
     * @param clazz 类
     * @return Page 分页
     */
    public PageModel findPage(Query query, PageModel page, Class<?> clazz) {

        // 查询出一共的条数
        Long count = mongoTemplate.count(query, clazz);
        // 查询pagge
        query.skip((page.getNowPage() - 1) * page.getPagesize());// 从那条记录开始
        query.limit(page.getPagesize());// 取多少条记录

        if (!query.isSorted()) {
            query.with(Sort.by(Sort.Direction.DESC, "id"));
        }
        mongoSqlLogServer.logQuery(clazz, query);
        List<?> list = mongoTemplate.find(query, clazz);

        page.setTotal(count);
        page.setList(list);

        return page;
    }

    /**
     * 按查询条件获取Page
     *
     * @param criteria 查询
     * @param page     分页
     * @param clazz    类
     * @return Page 分页
     */
    public PageModel findPage(Criteria criteria, PageModel page, Class<?> clazz) {
        return findPage(new Query(criteria), page, clazz);
    }

    /**
     * 按查询条件获取Page
     *
     * @param criteriaWrapper 查询
     * @param page     分页
     * @param clazz    类
     * @return Page 分页
     */
    public PageModel findPage(CriteriaWrapper criteriaWrapper, PageModel page, Class<?> clazz) {
        return findPage(new Query(criteriaWrapper.build()), page, clazz);
    }

    /**
     * 按查询条件获取Page
     *
     * @param criteria 查询
     * @param sort     排序
     * @param clazz    类
     * @return Page 分页
     */
    public PageModel findPage(Criteria criteria, Sort sort, PageModel page, Class<?> clazz) {
        return findPage(new Query(criteria).with(sort), page, clazz);
    }

    /**
     * 按查询条件获取Page
     *
     * @param criteriaWrapper 查询
     * @param sort     排序
     * @param clazz    类
     * @return Page 分页
     */
    public PageModel findPage(CriteriaWrapper criteriaWrapper, Sort sort, PageModel page, Class<?> clazz) {
        return findPage(new Query(criteriaWrapper.build()).with(sort), page, clazz);
    }

    /**
     * 按查询条件获取Page
     *
     * @param sort     排序
     * @param clazz    类
     * @return Page 分页
     */
    public PageModel findPage(Sort sort, PageModel page, Class<?> clazz) {
        return findPage(new Query().with(sort), page, clazz);
    }

    /**
     * 获取Page
     *
     * @param page  分页
     * @param clazz 类
     * @return Page 分页
     */
    public PageModel findPage(PageModel page, Class<?> clazz) {
        return findPage(new Query(), page, clazz);
    }

    /**
     * 根据id查找
     *
     * @param id    id
     * @param clazz 类
     * @return T 对象
     */
    public <T> T findById(String id, Class<T> clazz) {

        if (StrUtil.isEmpty(id)) {
            return null;
        }

        mongoSqlLogServer.logQuery(clazz, new Query(Criteria.where("id").is(id)));
        return (T) mongoTemplate.findById(id, clazz);
    }

    /**
     * 根据条件查找单个
     *
     * @param query 查询
     * @param clazz 类
     * @return T 对象
     */
    public <T> T findOneByQuery(Query query, Class<T> clazz) {
        query.limit(1);
        mongoSqlLogServer.logQuery(clazz, query);
        return (T) mongoTemplate.findOne(query, clazz);
    }

    /**
     * 根据条件查找单个
     *
     * @param <T>      类型
     * @param criteria
     * @param clazz    类
     * @return T 对象
     */
    public <T> T findOneByQuery(Criteria criteria, Class<T> clazz) {
        return (T) findOneByQuery(new Query(criteria), clazz);
    }

    /**
     * 根据条件查找单个
     *
     * @param <T>      类型
     * @param criteriaWrapper
     * @param clazz    类
     * @return T 对象
     */
    public <T> T findOneByQuery(CriteriaWrapper criteriaWrapper, Class<T> clazz) {
        return (T) findOneByQuery(new Query(criteriaWrapper.build()), clazz);
    }

    /**
     * 根据条件查找单个
     *
     * @param criteria 查询
     * @param clazz 类
     * @return T 对象
     */
    public <T> T findOneByQuery(Criteria criteria, Sort sort, Class<T> clazz) {
        return (T) findOneByQuery(new Query(criteria).with(sort), clazz);
    }

    /**
     * 根据条件查找单个
     *
     * @param criteriaWrapper 查询
     * @param clazz 类
     * @return T 对象
     */
    public <T> T findOneByQuery(CriteriaWrapper criteriaWrapper, Sort sort, Class<T> clazz) {
        return (T) findOneByQuery(new Query(criteriaWrapper.build()).with(sort), clazz);
    }

    /**
     * 根据条件查找单个
     *
     * @param clazz 类
     * @return T 对象
     */
    public <T> T findOneByQuery(Sort sort, Class<T> clazz) {
        return (T) findOneByQuery(new Query().with(sort), clazz);
    }

    /**
     * 根据条件查找List
     *
     * @param <T>   类型
     * @param query 查询
     * @param clazz 类
     * @return List 列表
     */
    public <T> List<T> findListByQuery(Query query, Class<T> clazz) {
        if (!query.isSorted()) {
            query.with(Sort.by(Sort.Direction.DESC, "id"));
        }
        mongoSqlLogServer.logQuery(clazz, query);
        return (List<T>) mongoTemplate.find(query, clazz);
    }

    /**
     * 根据条件查找List
     *
     * @param <T>      类型
     * @param criteria 查询
     * @param clazz    类
     * @return List 列表
     */
    public <T> List<T> findListByQuery(Criteria criteria, Class<T> clazz) {
        return (List<T>) findListByQuery(new Query(criteria), clazz);
    }

    /**
     * 根据条件查找List
     *
     * @param <T>      类型
     * @param criteriaWrapper 查询
     * @param clazz    类
     * @return List 列表
     */
    public <T> List<T> findListByQuery(CriteriaWrapper criteriaWrapper, Class<T> clazz) {
        return (List<T>) findListByQuery(new Query(criteriaWrapper.build()), clazz);
    }

    /**
     * 根据条件查找List
     *
     * @param <T>      类型
     * @param criteria 查询
     * @param sort     排序
     * @param clazz    类
     * @return List 列表
     */
    public <T> List<T> findListByQuery(Criteria criteria, Sort sort, Class<T> clazz) {
        return (List<T>) findListByQuery(new Query(criteria).with(sort), clazz);
    }

    /**
     * 根据条件查找List
     *
     * @param <T>      类型
     * @param criteriaWrapper 查询
     * @param sort     排序
     * @param clazz    类
     * @return List 列表
     */
    public <T> List<T> findListByQuery(CriteriaWrapper criteriaWrapper, Sort sort, Class<T> clazz) {
        return (List<T>) findListByQuery(new Query(criteriaWrapper.build()).with(sort), clazz);
    }

    /**
     * 根据条件查找某个属性
     *
     * @param <T>           类型
     * @param query         查询
     * @param documentClass 类
     * @param property      属性
     * @param propertyClass 属性类
     * @return List 列表
     */
    public <T> List<T> findPropertiesByQuery(Query query, Class<?> documentClass, String property, Class<T> propertyClass) {
        query.fields().include(property);
        List<?> list = findListByQuery(query, documentClass);
        List<T> propertyList = extractProperty(list, property, propertyClass);

        return propertyList;
    }

    /**
     * 根据条件查找某个属性
     *
     * @param query      查询
     * @param documentClass 类
     * @param property      属性
     * @return List 列表
     */
    public List<String> findPropertiesByQuery(Query query, Class<?> documentClass, String property) {
        return findPropertiesByQuery(query, documentClass, property, String.class);
    }

    /**
     * 根据条件查找某个属性
     *
     * @param <T>           类型
     * @param criteria      查询
     * @param documentClass 类
     * @param property      属性
     * @param propertyClass 属性类
     * @return List 列表
     */
    public <T> List<T> findPropertiesByQuery(Criteria criteria, Class<?> documentClass, String property, Class<T> propertyClass) {
        return (List<T>) findPropertiesByQuery(new Query(criteria), documentClass, property, propertyClass);
    }

    /**
     * 根据条件查找某个属性
     *
     * @param <T>           类型
     * @param criteriaWrapper      查询
     * @param documentClass 类
     * @param property      属性
     * @param propertyClass 属性类
     * @return List 列表
     */
    public <T> List<T> findPropertiesByQuery(CriteriaWrapper criteriaWrapper, Class<?> documentClass, String property, Class<T> propertyClass) {
        return (List<T>) findPropertiesByQuery(new Query(criteriaWrapper.build()), documentClass, property, propertyClass);
    }

    /**
     * 根据条件查找某个属性
     *
     * @param criteria      查询
     * @param documentClass 类
     * @param property      属性
     * @return List 列表
     */
    public List<String> findPropertiesByQuery(Criteria criteria, Class<?> documentClass, String property) {
        return findPropertiesByQuery(new Query(criteria), documentClass, property, String.class);
    }

    /**
     * 根据条件查找某个属性
     *
     * @param criteriaWrapper    查询
     * @param documentClass 类
     * @param property      属性
     * @return List 列表
     */
    public List<String> findPropertiesByQuery(CriteriaWrapper criteriaWrapper, Class<?> documentClass, String property) {
        return findPropertiesByQuery(new Query(criteriaWrapper.build()), documentClass, property, String.class);
    }


    /**
     * 根据条件查找某个属性
     *
     * @param documentClass 类
     * @param property      属性
     * @return List 列表
     */
    public List<String> findPropertiesByIds(Collection<String> ids, Class<?> documentClass, String property) {
        return findPropertiesByQuery(Criteria.where("id").in(ids), documentClass, property, String.class);
    }



    /**
     * 根据条件查找id
     *
     * @param query 查询
     * @param clazz 类
     * @return List 列表
     */
    public List<String> findIdsByQuery(Query query, Class<?> clazz) {
        Set<String> ids = new HashSet<String>();
        query.fields().include("id");
        mongoSqlLogServer.logQuery(clazz, query);
        List<?> list = mongoTemplate.find(query, clazz);
        for (Object object : list) {
            ids.add((String) ReflectUtil.getFieldValue(object, "id"));
        }

        return new ArrayList<String>(ids);
    }

    /**
     * 根据条件查找id
     *
     * @param criteria 查询
     * @param clazz    类
     * @return List 列表
     */
    public List<String> findIdsByQuery(Criteria criteria, Class<?> clazz) {
        return findIdsByQuery(new Query(criteria), clazz);
    }

    /**
     * 根据条件查找id
     *
     * @param criteriaWrapper 查询
     * @param clazz    类
     * @return List 列表
     */
    public List<String> findIdsByQuery(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
        return findIdsByQuery(new Query(criteriaWrapper.build()), clazz);
    }

    /**
     * 根据条件查找id
     *
     * @param criteria 查询
     * @param clazz    类
     * @return List 列表
     */
    public List<String> findIdsByQuery(Criteria criteria, Sort sort, Class<?> clazz) {
        return findIdsByQuery(new Query(criteria).with(sort), clazz);
    }

    /**
     * 根据条件查找id
     *
     * @param criteriaWrapper 查询
     * @param clazz    类
     * @return List 列表
     */
    public List<String> findIdsByQuery(CriteriaWrapper criteriaWrapper, Sort sort, Class<?> clazz) {
        return findIdsByQuery(new Query(criteriaWrapper.build()).with(sort), clazz);
    }

    /**
     * 根据id集合查找
     *
     * @param ids id集合
     * @param clazz 类
     * @return List 列表
     */
    public <T> List<T> findListByIds(Collection<String> ids, Class<T> clazz) {
        return findListByQuery(new Query(Criteria.where("id").in(ids)), clazz);
    }

    /**
     * 根据id集合查找
     *
     * @param ids id集合
     * @param clazz 类
     * @return List 列表
     */
    public <T> List<T> findListByIds(Collection<String> ids, Sort sort, Class<T> clazz) {
        return findListByQuery(new Query(Criteria.where("id").in(ids)).with(sort), clazz);
    }

    /**
     * 查询全部
     *
     * @param <T>   类型
     * @param clazz 类
     * @return List 列表
     */
    public <T> List<T> findAll(Class<T> clazz) {
        return findListByQuery(new Query(), clazz);
    }

    /**
     * 查询全部
     *
     * @param <T>   类型
     * @param clazz 类
     * @return List 列表
     */
    public <T> List<T> findAll(Sort sort, Class<T> clazz) {
        return findListByQuery(new Criteria(), sort, clazz);
    }

    /**
     * 查找全部的id
     *
     * @param clazz 类
     * @return List 列表
     */
    public List<String> findAllIds(Class<?> clazz) {
        return findIdsByQuery(new Query(), clazz);
    }

    /**
     * 查找数量
     *
     * @param query 查询
     * @param clazz 类
     * @return Long 数量
     */
    public Long findCountByQuery(Query query, Class<?> clazz) {
        mongoSqlLogServer.logCount(clazz, query);
        return mongoTemplate.count(query, clazz);
    }

    /**
     * 查找数量
     *
     * @param criteria 查询
     * @param clazz    类
     * @return Long 数量
     */
    public Long findCountByQuery(Criteria criteria, Class<?> clazz) {
        return findCountByQuery(new Query(criteria), clazz);
    }

    /**
     * 查找数量
     *
     * @param criteriaWrapper 查询
     * @param clazz    类
     * @return Long 数量
     */
    public Long findCountByQuery(CriteriaWrapper criteriaWrapper, Class<?> clazz) {
        return findCountByQuery(new Query(criteriaWrapper.build()), clazz);
    }

    /**
     * 查找全部数量
     *
     * @param clazz 类
     * @return Long 数量
     */
    public Long findAllCount(Class<?> clazz) {
        return findCountByQuery(new Query(), clazz);
    }

    /**
     * 获取list中对象某个属性,组成新的list
     *
     * @param list     列表
     * @param clazz    类
     * @param property 属性
     * @return List<T> 列表
     */
    private <T> List<T> extractProperty(List<?> list, String property, Class<T> clazz) {
        Set<T> rs = new HashSet<T>();
        for (Object object : list) {
            Object value = ReflectUtil.getFieldValue(object, property);
            if (value.getClass().equals(clazz)) {
                rs.add((T) value);
            }
        }

        return new ArrayList<T>(rs);
    }
}
